package model;

import java.io.Serializable;

import ar.edu.unq.tpi.pconc.Channel;

public class Unidad extends Thread implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 2656442992144121695L;
	
	private int nivel;
	private int combatesGanados;
	private Castillo castillo;
	private Ciudad ciudadActual;
	private Ciudad ciudadDestino;
	private boolean estoyVivo;
	private Integer identificador;
	private boolean ganeElJuego = false;

	private boolean esperandoRespuesta;

	private int cantCombatesParaProximoNivel;

	private Integer canal;
	
	public Unidad(Castillo castillo, Integer identificador) {
		this.estoyVivo = true;
		this.setNivel(1);
		this.setCombatesGanados(0);
		this.setIdentificador(identificador);
		this.setCastillo(castillo);
		this.setCiudadActual(castillo.getCiudad());
		this.canal = this.getCastillo().getCanal() + this.getIdentificador();
	}
	
	@Override
	public String toString() {
		return "Unidad " + this.getIdentificador() + " del " + this.castillo;
	}

	/**
	 * Logica de como se comporta la Unidad.
	 */
	public void run() {
		// Mientras este vivo Y NO GANÉ el juego hago algo...
		this.log("Nacio (Inicia el RUN)");
		Mensaje unMensaje = null;
		this.esperandoRespuesta = false;
		Channel<Mensaje> oreja = new Channel<Mensaje>(this.getCanal());
		Channel<String> murio = new Channel<String>(2000 + this.getCanal());
		murio.send("");

		while (this.sigoViviendo(murio.receive()) && !this.ganeElJuego && !Mapa.gameOver()) {
			System.err.println("Paso una vez");
			if(this.esperandoRespuesta) {
				this.log("Esperando una orden o respuesta en el canal: "+this.getCanal());
				unMensaje = oreja.receive();
				switch (unMensaje) {
					case ENTRAR:
						this.log("Recibi la orden de entrar a " + this.getCiudadDestino());
						this.entrar();
						break;
					case SALIR:
						this.log("Recibi la orden de salir de " + this.getCiudadActual());
						this.salirDeCiudad();
						break;
					case GANO_BATALLA:
						this.log("Recibi la respuesta de que gane la batalla :)");
						this.ganar();
						break;
					case GANO_PARTIDA:
						this.ganeElJuego = true;
						break;
				default:
					break;
				}
				
			} else {
				this.log("Estoy en " + this.getCiudadActual() + ". No tengo nada para hacer, paso a otra ciudad.");
				pasarAOtraCiudad();
			}
			murio.send("");
			try {
				// TODO: Sacar! LO duermo un rato sino es un paseador compulsivo!
				sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
		}
		
		if (this.estoyVivo){
			if (!Mapa.isHuboGanador()){
				this.log("Gane el juego");
				Mapa.setHuboGanador(true);
				Mapa.posicionarUnidad(this, this.getCiudadActual());
			} else {
				this.log("Perdí");
			}
				
		} else {
			this.log("Definitivamente murió");
		}
	}

	private boolean sigoViviendo(String receive) {
		if(receive.equals("murio")){
			this.log("Recibi la noticia de que perdi la batalla :(".toUpperCase() + this.estoyVivo);
			this.morir();
			//this.castillo.unidadMuerta();
			return false;
		} else {
			return true;
		}
		
	}

	private void actualizarNivel() {
		if (this.getCombatesGanados() == this.cantCombatesParaProximoNivel){
			this.setNivel(this.getNivel() + 1);
			this.cantCombatesParaProximoNivel = Unidad.fib(this.getNivel());
		}
	}

	private void log(String string) {
		System.out.println("[" + this + " ("+ this.getCanal() +")] : " + string);
	}
	
	// ---------------------------------------------------------------------------
	// Comportamiento de la Unidad.
	// ---------------------------------------------------------------------------

	private void entrar() {
		this.getCiudadActual().solicitarSalidaDeCiudad(this);
		setCiudadActual(getCiudadDestino());
		this.esperandoRespuesta = false;
		if (this.getCiudadActual().tieneCastillo() && !(this.getCiudadActual().getCastillo().toString().equals(this.getCastillo().toString()))){
			this.ganoPartida();
			System.err.println("Hey!... soy " + this.getCastillo().getColor() + " y llegue al castillo " + this.getCiudadActual().getCastillo().getColor());
			this.esperandoRespuesta = false;
		}
		setCiudadDestino(null);
		this.esperandoRespuesta = false;
	}
	
	private void morir() {
		this.estoyVivo = false;
		this.esperandoRespuesta = false;
//		this.setCiudadActual(null);
		this.castillo.msgCrearUnidad();
		Mapa.quitarUnidad(this);
	}
	
	private void ganar() {
		this.combatesGanados++;
		this.actualizarNivel();
		this.esperandoRespuesta = false;
	}

	private void pasarAOtraCiudad() {
		setCiudadDestino(this.elegirProximaCiudad());
//		if (this.getCiudadDestino().tieneCastillo() && (this.getCiudadDestino().getCastillo().toString().equals(this.getCastillo().toString()))){
//			this.log("Voy de " + this.getCiudadActual() + " a " + this.getCiudadDestino());
//			getCiudadDestino().solicitarIngresoACiudad(this);
//		} else {
		this.log("Voy de " + this.getCiudadActual() + " a " + this.getCiudadDestino());
		getCiudadDestino().solicitarIngresoACiudad(this);
		this.esperandoRespuesta = true;			
//		}
	}
	
	/**
	 * Tiene la logica que entiende cuales son las ciudades destino y hace un Random para elegirlas.
	 * @return
	 */
	private Ciudad elegirProximaCiudad() {
		this.log("Tengo " + this.ciudadActual.getCiudadesConectadas().size() + " ciudades para elegir: " + this.ciudadActual.getCiudadesConectadas() );
		Integer idCiudad = (int) (Math.random() * this.ciudadActual.getCiudadesConectadas().size());
		Ciudad unaCiudad = this.ciudadActual.getCiudadesConectadas().get(idCiudad);
		this.log("Seleccione pasar a la ciudad " + unaCiudad);
		return unaCiudad;
	}
	
	public void salirDeCiudad(){
		this.ciudadActual = this.ciudadDestino;
		this.esperandoRespuesta = false;
	}
	

	// ---------------------------------------
	// Setters y Getters
	// ---------------------------------------

	public int getNivel() {
		return nivel;
	}
	
	public void setNivel(int nivel) {
		this.nivel = nivel;
	}

	public Castillo getCastillo() {
		return castillo;
	}

	public void setCastillo(Castillo castillo) {
		this.castillo = castillo;
	}

	public int getCombatesGanados() {
		return combatesGanados;
	}

	public void setCombatesGanados(int combatesGanados) {
		this.combatesGanados = combatesGanados;
	}

	public Ciudad getCiudadActual() {
		return ciudadActual;
	}

	public void setCiudadActual(Ciudad ciudadActual) {
		this.ciudadActual = ciudadActual;
		Mapa.posicionarUnidad(this, ciudadActual);
	}

	public Ciudad getCiudadDestino() {
		return ciudadDestino;
	}

	public void setCiudadDestino(Ciudad ciudadDestino) {
		this.ciudadDestino = ciudadDestino;
	}

	public String getIdParaUI() {
		return this.getCastillo().getColor() + this.getIdentificador();
	}
	
	// ---------------------------------------------------------------------------
	// Mensajes especificos que saben como interactuar con el thread.
	// Solo usados sobre la copia.
	// ---------------------------------------------------------------------------
	
	/**
	 * Mensajes que entiende una unidad
	 * @author gpilla
	 *
	 */
	public enum Mensaje{
	    GANO_BATALLA, ENTRAR, SALIR, GANO_PARTIDA
	}
	
	public void permitirEntrada()
	{
		Channel<Mensaje> oreja = new Channel<Mensaje>(this.getCanal());
		oreja.send(Mensaje.ENTRAR);
	}
	
	public void permitirSalida()
	{
		Channel<Mensaje> oreja = new Channel<Mensaje>(this.getCanal());
		oreja.send(Mensaje.SALIR);
	}
	
	public void avisarQueGano() {
		this.log("");
		System.err.println(this.toString() + "Me avisaron que gané la batalla");
		Channel<Mensaje> instanciaCanal = new Channel<Mensaje>(this.getCanal());
		instanciaCanal.send(Mensaje.GANO_BATALLA);
	}

	public void avisarQuePerdio()  {
//		Channel<Mensaje> oreja = new Channel<Mensaje>(this.canal);
//		oreja.send(Mensaje.PERDIO_BATALLA);
		System.err.println("Se avisa a la unidad [" + this +  "] que perdio la batalla");
		this.estoyVivo = false;
		Channel<String> murio = new Channel<String>(2000+this.getCanal());
		murio.send("murio");
	}
	
	public void ganoPartida(){
		this.ganeElJuego = true;
		Channel<Mensaje> oreja = new Channel<Mensaje>(this.getCanal());
		oreja.send(Mensaje.GANO_PARTIDA);
	}

	public Integer getIdentificador() {
		return identificador;
	}

	public void setIdentificador(Integer identificador) {
		this.identificador = identificador;
	}


	@Override
	public boolean equals(Object unidad) {
		this.log("Me comparo con :" + unidad );
		return this.toString() == unidad.toString();
	}

	public static int fib(int nivel) { 
		if (nivel <= 1){ 
			return nivel;
		} 
		else return fib(nivel-1) + fib(nivel-2); 
	} 

	public Integer getCanal() {
		return this.canal;
	}

	
}